﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Data;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Kugar.Core.BaseStruct;
using Kugar.Core.Configuration;
using Kugar.Core.Data;
using Kugar.Core.Data.DBCommon;
using Kugar.Tool.DataSync.ClientPlugin;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Scripting;
using Microsoft.CodeAnalysis.Scripting.Hosting;
using Newtonsoft.Json.Linq;
using Kugar.Core.ExtMethod;
using Kugar.Core.Network;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
namespace Kugar.Tool.DataSync.Client.BLL
{
    /// <summary>
    /// 通过.cs脚本处理导入操作
    /// </summary>
    /// <see cref="https://github.com/dotnet/roslyn/wiki/Scripting-API-Samples"/>
    [Export(typeof(IImporterPlugin))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class ScriptImportPlugin : IImporterPlugin
    {
        private string _scriptFolder = "";
        private Dictionary<string, ScriptRunner<ResultReturn>> _cacheScriptRunners = new Dictionary<string, ScriptRunner<ResultReturn>>(StringComparer.CurrentCultureIgnoreCase);
        private static IRecyclablePool<IKugarConnection> _pool = null;
        private static ScriptWebHelper _webHelper = new ScriptWebHelper();

        static ScriptImportPlugin()
        {
            var dbTypeStr = CustomConfigManager.Default.AppSettings.GetValueByName("DbType");

            DatabaseType? dbType = null;

            if (string.IsNullOrWhiteSpace(dbTypeStr))
            {
                dbType = (DatabaseType)Enum.Parse(typeof(DatabaseType), dbTypeStr, true);
            }

            var connStr = CustomConfigManager.Default.AppSettings.GetValueByName("ConnectString");

            if (dbType.HasValue && !string.IsNullOrWhiteSpace(connStr))
            {
                _pool = DatabaseHelperFactory.CreatePool(dbType.Value, connStr);
            }
        }

        public ScriptImportPlugin()
        {
            _scriptFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "scripts");

            var files = Directory.GetFiles(_scriptFolder, "import.*.cs");

            var loader = new InteractiveAssemblyLoader();

            string[] namespances = null;
            string[] assemblies = null;

            var configJsonPath = Path.Combine(_scriptFolder, "config.json");
            if (File.Exists(configJsonPath))
            {
                var configJson = JObject.Parse(File.ReadAllText(configJsonPath, Encoding.UTF8));

                namespances = configJson.GetJArray("ImportNamespaces").Select(x => x.ToString()).ToArray();
                assemblies = configJson.GetJArray("ReferenceAssembly").Select(x => x.ToString()).ToArray();
            }

            var opt = ScriptOptions.Default.AddImports(namespances);
            
            var alist = AppDomain.CurrentDomain.GetAssemblies();

            foreach (var assebmly in alist)
            {
                loader.RegisterDependency(assebmly);

                try
                {
                    opt=opt.AddReferences(assebmly);
                }
                catch (Exception)
                {
                    
                }
            }

            if (assemblies.HasData())
            {
                ScriptOptions.Default.AddReferences(assemblies);
            }


            foreach (var filePath in files)
            {
                //文件名格式为:  import.{DataTypeName}.{optType}.cs
                /*
                    import.{DataTypeName}.cs  //导入的脚本
                 */

                var scriptStr = File.ReadAllText(filePath);

                var script = CSharpScript.Create<ResultReturn>(scriptStr,options:opt, assemblyLoader: loader, globalsType: typeof(ImportScriptGlobalValue));

                var runer = script.CreateDelegate();

                //var fileNameParser=new ScriptFileNameParser(Path.GetFileName(filePath));

                var dataTypeName = Path.GetFileNameWithoutExtension(filePath);

                _cacheScriptRunners.Add(dataTypeName, runer);
            }
        }

        public ResultReturn Import(string dataTypeName, JObject data)
        {
            var scriptFileName = $"import.{dataTypeName}";

            var runner = _cacheScriptRunners.TryGetValue(scriptFileName);

            if (runner == null)
            {
                return new FailResultReturn("不存在可处理的脚本");
            }

            var db = _pool.Take();

            try
            {
                runner(new ImportScriptGlobalValue(data, db)).Wait(new TimeSpan(0, 0, 30));

                return new SuccessResuleReturn();
            }
            catch (Exception ex)
            {
                return new FailResultReturn(ex);
            }
            finally
            {
                db.Dispose();
            }
        }


        public bool CanHandleWith(string dataTypeName)
        {
            foreach (var key in _cacheScriptRunners.Keys)
            {
                if (key.Contains($".{dataTypeName}.", true))
                {
                    return true;
                }
            }

            return false;
        }

        public void Close()
        {
            _pool?.Close();
            _pool?.Dispose();
        }

        public class ImportScriptGlobalValue : DynamicObject
        {
            private JObject _data = null;
            private IKugarConnection _dbHelper = null;

            internal ImportScriptGlobalValue(JObject data, IKugarConnection dbHelper)
            {
                _data = data;
                _dbHelper = dbHelper;
            }

            public override bool TryGetMember(GetMemberBinder binder, out object result)
            {
                JToken v;

                if (binder.Name == "WebHelper")
                {
                    result = _webHelper;

                    return true;
                }

                if (binder.Name == "Database")
                {
                    result = _dbHelper;

                    return true;
                }

                if (_data.TryGetValue(binder.Name, out v))
                {
                    result = v;

                    return true;
                }
                else
                {
                    result = null;
                    return false;
                }

            }
        }
    }

    /// <summary>
    /// 通过.cs脚本处理导出操作
    /// </summary>
    /// <see cref="https://github.com/dotnet/roslyn/wiki/Scripting-API-Samples"/>
    [Export(typeof(IExporterPlugin))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class ScriptExportPlugin : ExporterPluginBase
    {
        private string _scriptFolder = "";
        private Dictionary<string, ScriptRunner<ResultReturn>> _cacheScriptRunners = new Dictionary<string, ScriptRunner<ResultReturn>>(StringComparer.CurrentCultureIgnoreCase);
        private static ScriptWebHelper _webHelper = new ScriptWebHelper();
        private static IRecyclablePool<IKugarConnection> _pool = null;

        static ScriptExportPlugin()
        {
            var dbTypeStr = CustomConfigManager.Default.AppSettings.GetValueByName("DbType");

            DatabaseType? dbType = null;

            if (!string.IsNullOrWhiteSpace(dbTypeStr))
            {
                dbType = (DatabaseType)Enum.Parse(typeof(DatabaseType), dbTypeStr, true);
            }

            var connStr = CustomConfigManager.Default.AppSettings.GetValueByName("ConnectString");

            if (dbType.HasValue && !string.IsNullOrWhiteSpace(connStr))
            {
                _pool = DatabaseHelperFactory.CreatePool(dbType.Value, connStr);
            }
        }

        internal ScriptExportPlugin()
        {
            _scriptFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "scripts");
            
            var files = Directory.GetFiles(_scriptFolder, "export.*.cs");

            var loader = new InteractiveAssemblyLoader(new MetadataShadowCopyProvider() {});
            
            string[] namespances = null;
            string[] assemblies = null;

            var configJsonPath = Path.Combine(_scriptFolder, "config.json");
            if (File.Exists(configJsonPath))
            {
                var configJson = JObject.Parse(File.ReadAllText(configJsonPath, Encoding.UTF8));

                namespances = configJson.GetJArray("ImportNamespaces").Select(x => x.ToString()).ToArray();
                assemblies = configJson.GetJArray("ReferenceAssembly").Select(x => x.ToString()).ToArray();
            }

            var opt = ScriptOptions.Default.AddImports(namespances);
            
            var alist = AppDomain.CurrentDomain.GetAssemblies();

            foreach (var assebmly in alist)
            {
                loader.RegisterDependency(assebmly);

                try
                {
                    opt=opt.AddReferences(assebmly);
                }
                catch (Exception)
                {
                    
                }
            }

            if (assemblies.HasData())
            {
                ScriptOptions.Default.AddReferences(assemblies);
            }

            foreach (var filePath in files)
            {
                //文件名格式为:  import.{DataTypeName}.{optType}.cs
                /*
                    export.{DataTypeName}.update.cs  //导入的更新脚本
                    export.{DataTypeName}.remove.cs //导入的删除脚本
                    export.{DataTypeName}.add.cs       //导入的新增脚本
                 */

                var scriptStr = File.ReadAllText(filePath);
               
                var script = CSharpScript.Create<ResultReturn>(scriptStr,options:opt,assemblyLoader:loader, globalsType: typeof(ExportScriptGlobalValue));

                var runer = script.CreateDelegate();

                var dataTypeName = Path.GetFileNameWithoutExtension(filePath);

                _cacheScriptRunners.Add(dataTypeName, runer);
            }

            if (_cacheScriptRunners.HasData())
            {
                TimerPool.Default.Add(60 * 1000, onTimer);
            }
        }

        private void onTimer(object state)
        {
            foreach (var runner in _cacheScriptRunners)
            {
                var db = _pool?.Take();

                try
                {
                    var e = new ExportScriptGlobalValue(db);

                    runner.Value(e);

                    if (e.ExportData.HasData())
                    {
                        SendData(runner.Key, e.ExportData);
                    }
                }
                catch (Exception)
                {
                    //TODO:记录错误信息
                    continue;
                }
                finally
                {
                    db.Dispose();
                }

            }
        }

        public override void Close()
        {
            base.Close();

            _pool?.Close();
            _pool?.Dispose();
        }

        public class ExportScriptGlobalValue
        {
            private IKugarConnection _dbHelper = null;

            internal ExportScriptGlobalValue(IKugarConnection dbHelper)
            {
                _dbHelper = dbHelper;
            }

            public JObject ExportData { get; private set; } = new JObject();

            public ScriptWebHelper WebHelper => _webHelper;

            public IKugarConnection Database => _dbHelper;
        }

    }

    public class ScriptDBHelper : IDisposable
    {
        private IKugarConnection _helper = null;

        internal ScriptDBHelper(IKugarConnection helper)
        {
            _helper = helper;
        }

        public DataTable Query(string sql)
        {
            return _helper.ExecuteDataTable(sql);
        }

        public int Execute(string sql)
        {
            return _helper.ExecuteNoQuery(sql);
        }

        public VM_PagedDataTable GetPagedList(string sql, int pageIndex, int pageSize)
        {
            return _helper.ExecutePagingDataTable(new PagingArgs()
            {
                ColumnSql = "*",
                TableSql = sql,
                PageIndex = pageIndex,
                PageSize = pageSize
            });
        }

        public void Dispose()
        {
            _helper.Dispose();
        }
    }

    public class ScriptWebHelper
    {
        public string Get(string url)
        {
            return WebHelper.GetUriData(url, encoding: Encoding.UTF8);
        }

        public JObject GetJson(string url)
        {
            var jsonStr = Get(url);

            return JObject.Parse(jsonStr);
        }

        public string Post(string url, string data)
        {
            return WebHelper.GetUriDataByPost(url, data);
        }

        public JObject PostJson(string url, JObject data)
        {
            var jsonStr = Post(url, data.ToString(Formatting.None));

            return JObject.Parse(jsonStr);
        }
    }

    public class ScriptFileNameParser
    {
        public ScriptFileNameParser(string fileName)
        {

        }

        public string DataTypeName { private set; get; }

        public ScriptType Type { get; private set; }

        public enum ScriptType
        {
            Import,

            Export
        }
    }
}
